﻿using System;
using System.Configuration;
using System.Collections.Generic;
using System.Threading;
using System.Reflection;
using System.Data.Common;

//自定义命名空间
using DreamCube.Foundation.Basic.Utility;
using DreamCube.Foundation.Basic.Objects.EqualityComparers;

namespace DreamCube.Framework.DataAccess.Basic
{
    /// <summary>
    /// 数据库管理类；
    /// 数据库管理类加载数据库对象时，
    /// 都是从应用程序的config文件的connectionString节点获得连接字符串来创建的
    /// </summary>
    public static class DBManager
    {
        #region　"字段"

        /// <summary>
        /// 缓存数据库的实例
        /// </summary>
        private static Dictionary<String, Database> dbList =  new Dictionary<String, Database>(new StringEqualityIgnoreCaseComparerGeneric());

        #endregion

        /// <summary>
        /// 根据连接字符串对象获取Database对象
        /// </summary>
        /// <param name="connectionString"></param>
        /// <returns></returns>
        public static Database GetDBByConnectionString(ConnectionString connectionString)
        {
            if (connectionString != null)
                return GetDBByConnectionString(connectionString.ToString(), connectionString.DBProviderType);
            return null;
        }

        /// <summary>
        /// 根据链接字符串获得Database对象
        /// </summary>
        /// <param name="connectionStr"></param>
        /// <param name="providerType"></param>
        /// <returns></returns>
        public static Database GetDBByConnectionString(String connectionStr, DBProviderType providerType)
        {
            return GetDBByConnectionString(connectionStr, providerType.ToString());
        }

        /// <summary>
        /// 根据链接字符串获得Database对象
        /// </summary>
        /// <param name="connectionStr"></param>
        /// <param name="providerType"></param>
        /// <returns></returns>
        public static Database GetDBByConnectionString(String connectionStr, String providerType = "SqlClient")
        {
            if (String.IsNullOrEmpty(connectionStr))
                throw new ArgumentNullException("connectionStr");
            Database db = null;
            if (dbList.TryGetValue(connectionStr, out db)) return db;
            Config.DBFactoryElement factoryElement = DBFactoryHelper.GetFactoryElementByProviderType(providerType);
            DBFactory factory = null;
            //if (factoryElement.DbFactoryType.TryCreateNewInstanceByTypeFullName<DBFactory>(out factory, true))
            if (MyReflection.TryCreateNewInstanceByTypeFullName(factoryElement.DBFactoryType, out factory, true))
                db = factory.CreateDB(connectionStr);
            else
                return null;
            //把数据库实例添加到缓存中
            AddDBToCache(db, connectionStr);
            return db;
        }

        /// <summary>
        /// 获取默认的数据库名
        /// 也就是获取配置文件中，ConnectionStrings的第一个ConnectionString节点内容
        /// </summary>
        /// <returns></returns>
        public static String GetDefaultDBName()
        {
            if (ConfigurationManager.ConnectionStrings.Count > 0)
                return ConfigurationManager.ConnectionStrings[0].Name;
            return String.Empty;
        }

        /// <summary>
        /// 根据app.config(web.config)中的ConnectionStrings配置的连接字符串name值，获取对应的数据库实例；
        /// 此方法启用了缓存机制，并且支持单例模式，一个数据库名字只创建一个数据库实例
        /// </summary>
        /// <param name="name">如果不传入数据库名，则默认获取配置文件中的第一个配置连接对象，如果没有配置任何的数据库连接信息，则抛出异常</param>
        /// <returns></returns>
        public static Database GetDBByName(String name = "")
        {
            if (String.IsNullOrEmpty(name))
            {
                if (ConfigurationManager.ConnectionStrings.Count > 0)
                    name = ConfigurationManager.ConnectionStrings[0].Name;
                else
                    throw new ArgumentNullException("name");
            }
            if (String.IsNullOrEmpty(name)) return null;
            Database db = null;
            if (dbList.TryGetValue(name, out db)) return db;
            ConnectionStringSettings connectionStringSettings = ConfigurationManager.ConnectionStrings[name];
            if (String.IsNullOrEmpty(connectionStringSettings.ProviderName))
                throw new ArgumentNullException("name", Properties.Resources.ExceptionNotSetProviderName);

            Config.DBFactoryElement factoryElement = DBFactoryHelper.GetFactoryElement(connectionStringSettings.ProviderName);
            DBFactory factory = null;
            if (MyReflection.TryCreateNewInstanceByTypeFullName(factoryElement.DBFactoryType, out factory, true))
                db = factory.CreateDB(connectionStringSettings.ConnectionString);
            else
                return null;
            //把数据库实例添加到缓存中
            AddDBToCache(db, name);
            return db;
        }

        /// <summary>
        /// 根据app.config(web.config)中的ConnectionStrings配置的连接字符串name值，获取对应的数据库实例；
        /// 此方法启用了缓存机制，并且支持单例模式，一个数据库名字只创建一个数据库实例
        /// </summary>
        /// <param name="name">如果不传入数据库名，则默认获取配置文件中的第一个配置连接对象，如果没有配置任何的数据库连接信息，则抛出异常</param>
        /// <returns></returns>
        public static T GetDBByName<T>(String name = "") where T : Database
        {
            Database db = GetDBByName(name);
            if (db != null) return db as T;
            return null;
        }

        /// <summary>
        /// 把数据库实例添加到缓存中
        /// </summary>
        /// <param name="db"></param>
        /// <param name="key"></param>
        private static void AddDBToCache(Database db, String key)
        {
            Int32 repeatTimes = 5;
            //把新建的对象添加到集合中
            //不断尝试多次把对象添加到缓冲区中
            while (!MyDictionary.TryAdd(dbList, key, db, Foundation.Basic.Enums.CollectionsAddOper.IgnoreIfExist) && repeatTimes > 0)
            {
                Database tempDB = null;
                //当把对象添加到缓冲区中失败时，则再尝试获取指定名字的数据库实例，获取失败时再创建
                if (dbList.TryGetValue(key, out tempDB))
                    break;
                repeatTimes--;
            }
        }
    }
}
